import struct
import argparse


MAGIC_NUMBER = 50988526717644704;
MAGIC_NUMBER2 = 2216409480638853224;


class ChunkPrefix:
    __CHUNK_PREFIX_FORMAT = "=QQIQQQ"; # MAGIC | MAGIC2 | TYPE | SIZE_IN_BLOCKS | SIZE_IN_BYTES | RANDOM_MAGIC
    __CHUNK_PREFIX_FORMAT_LEN = struct.calcsize( __CHUNK_PREFIX_FORMAT );
    
    @classmethod
    def AsCppStruct( cls ):
        return """
struct ChunkPrefix
{
    ChunkPrefix( ) :
        magic( MAGIC_NUMBER ),
        magic2( MAGIC_NUMBER2 ),
        type( 0 ),
        sizeInBlocks( 0 ),
        sizeInBytes( 0 ),
        randomMagic( 0 )
    {
    }
    
    bool TestMagics( ) const
    {
        if( magic != MAGIC_NUMBER )
            return false;
        if( magic2 != MAGIC_NUMBER2 )
            return false;
        return true;
    }

    unsigned long long magic;
    unsigned long long magic2;
    unsigned int type;
    unsigned long long sizeInBlocks;
    unsigned long long sizeInBytes;
    unsigned long long randomMagic;
}; 
""";
    
    def __init__( self ):
        self.magic = MAGIC_NUMBER;
        self.magic2 = MAGIC_NUMBER2;
        self.type = 0;
        self.sizeInBlocks = 0;
        self.sizeInBytes = 0;
        self.randomMagic = 0;
    
    def TestMagics( self ):
        if self.magic != MAGIC_NUMBER:
            return False;
        if self.magic2 != MAGIC_NUMBER2:
            return False;
        return True;


class ChunkSuffix:
    __CHUNK_SUFFIX_FORMAT = "=Q"; # RANDOM_MAGIC
    __CHUNK_SUFFIX_FORMAT_LEN = struct.calcsize( __CHUNK_SUFFIX_FORMAT );
    
    @classmethod
    def AsCppStruct( cls ):
        return """
struct ChunkSuffix
{
    ChunkSuffix( ) :
        randomMagic( 0 )
    {
    }

    unsigned long long randomMagic;
}; 
""";
    
    def __init__( self ):
        self.randomMagic = 0;


def TestChunkRandomMagics( chunkPrefix, chunkSuffix ):
    if not chunkPrefix.TestMagics( ):
        return False;
    if chunkPrefix.randomMagic != chunkSuffix.randomMagic:
        return False;
    return True;


def OutputCppDeclarations( outFileName ):
    fileToWrite = open( outFileName, "w+t" );
    
    out = "";
    
    out += """
// =====
// This file is AUTOGENERATED, do not modify
// =====

unsigned long long MAGIC_NUMBER = %(magic)s;
unsigned long long MAGIC_NUMBER2 = %(magic2)s;
""" % {"magic": MAGIC_NUMBER, "magic2": MAGIC_NUMBER2};
    
    out += ChunkPrefix.AsCppStruct( );
    out += ChunkSuffix.AsCppStruct( );
    
    out += """
bool TestChunkRandomMagics( const ChunkPrefix& chunkPrefix, const ChunkSuffix& chunkSuffix )
{
    if( !chunkPrefix.TestMagics( ) )
        return false;
    if( chunkPrefix.randomMagic != chunkSuffix.randomMagic )
        return false;
    return true;
}
""";
    
    fileToWrite.write( out );


def __main__( ):
    parser = argparse.ArgumentParser( description = 'Produce KeyValue File format declarations for C++' );
    parser.add_argument( "-l", "--lang", default = "cpp", choices = ["cpp"], help = "Language, if not set then cpp" );
    parser.add_argument( "-o", "--out", default = "format.h", help = "Output file, if not set then Format.h" );
    
    args = vars( parser.parse_args( ) );
    
    lang = args[ "lang" ];
    out = args[ "out" ];
    
    if lang == "cpp":
        OutputCppDeclarations( out );
    
    if "help" in args:
        print( parser.format_help( ) );


if __name__ == "__main__":
    __main__( );